/* eslint-disable */
import TreeNode from "./tree-node";
import Asserts from "../core/asserts";
import Layers from "../layers/index";
import {IllegalArgumentException} from "../core/exception";


/**
 * list -> tree 转换工具
 *
 * @constructor
 */
class TreeBuilder {
    /**
     * 父节点数组
     * @private
     */
    parents = [];

    /**
     * 子节点数组
     * @private
     */
    children = [];

    /**
     * 数据映射
     *
     * 将普通数据转换成标准树形结构
     *@private
     */
    mapper = null;

    /**
     * 设置映射函数
     * @param mapper 映射函数
     */
    setMapper(mapper) {
        this.mapper = mapper;
        return this;
    }

    /**
     * 映射数组
     *
     * @param list - 数组
     * @private
     * @returns 转换成 TreeNode 的数组
     */
    map(list) {
        if (Layers.isEmpty(list)) {
            return [];
        } else {
            let first = list[0];
            if (first instanceof TreeNode) {
                return list;
            } else {
                Asserts.notBlank(this.mapper, 'mapper is undefined!');
                let ret = [];
                for (let i = 0; i < list.length; i++) {
                    const node = new TreeNode();
                    this.mapper(list[i], node);
                    ret.push(node);
                }
                return ret;
            }
        }
    }

    /**
     * 设置父级数组
     *
     * @param parents - 父级数据列表
     * @returns 当前对象实例
     */
    setParents(parents) {
        this.parents = this.map(parents);
        return this;
    }

    /**
     * 设置子级数组
     *
     * @param children - 子级数据列表
     * @returns 当前对象实例
     */
    setChildren(children) {
        this.children = this.map(children);
        return this;
    }

    /**
     * 获取父级列表
     *
     * @returns 父级数据列表
     */
    getParents() {
        return this.parents;
    }

    /**
     * 获取子级列表
     *
     * @returns 子级数据列表
     */
    getChildren() {
        return this.children;
    }

    /**
     * 数据分组
     *
     * @returns 当前实例
     */
    groupBy() {
        if (this.parents === undefined && this.children === undefined) {
            throw new IllegalArgumentException("parents and children are both empty!");
        }

        if (this.parents === undefined) {
            this.parents = this.children;
        }

        if (this.children === undefined) {
            this.children = this.parents;
        }

        const parents = this.parents;
        const children = this.children;
        for (let i = 0; i < parents.length; i++) {
            for (let j = 0; j < children.length; j++) {
                let p = parents[i], c = children[j];
                // 此处可能发生空指针，注意初始化集合
                if (p.id === c.pid) {
                    // 子节点指向父节点
                    c.parent = p

                    // 父节点指向子节点
                    let list = p.children;
                    if (Layers.isBlank(list)) {
                        list = [];
                        p.children = list;
                    }
                    list.push(c);
                }
            }
        }

        return this;
    }

    /**
     * 如果已知要取的节点，直接按照节点 pid 进行获取
     *
     * @param pid - 父级ID，允许空值比较
     * @returns 满足条件的节点列表
     */
    getNodesByPid(pid) {
        const ret = [];
        const parents = this.parents;
        for (let i = 0; i < parents.length; i++) {
            let p = parents[i];
            if (p.pid === pid) {
                ret.push(p);
            }
        }
        return ret;
    }

    /**
     * 通过比较函数，不断比较，取出最顶级节点列表
     * <p>
     * 特例：如果存在 pid 为 undefined 的数据，则将该数据，作为最小的数据
     *
     * @param compare - 比较函数，返回 true 则取后一个 pid
     * @returns 满足条件的节点列表
     */
    getTopNodes(compare) {
        const parents = this.parents;
        if (parents.length === 1) {
            return [...parents]
        } else {
            let top = parents[0];
            let parent = undefined;
            for (let i = 1; i < parents.length; i++) {
                parent = parents[i];
                if (parent.pid === undefined) {
                    return this.getNodesByPid(undefined);
                } else {
                    if (compare(top, parent) < 0) {
                        top = parent;
                    }
                }
            }
            return this.getNodesByPid(top.pid);
        }
    }

    /**
     * 从组装的结果中取出 pid 最大的数据
     * <p>
     * 特例：如果存在 pid 为 undefined 的数据，则将该数据，作为最大的数据
     *
     * @return [TreeNode]
     */
    getByMaxPid() {
        return this.getTopNodes((a, b) => a.pid > b.pid ? 1 : 0);
    }

    /**
     * 从结果集中取出 pid 最小的数据
     * <p>
     * 特例：如果存在 pid 为 undefined 的数据，则将该数据，作为最小的数据
     *
     * @return [TreeNode]
     */
    getByMinPid() {
        return this.getTopNodes((a, b) => b.pid > a.pid ? 1 : 0);
    }

    /**
     * 随机取出一个元素，反复遍历树，直到取出最顶级的父节点。
     * <p>
     * 树状结构越深，需要遍历的次数越多。
     *
     * @return [TreeNode]
     */
    getRoot() {
        const parents = this.parents;
        // 以第一个节点的 pid 作为顶级节点，不断递归检索
        let top = parents[0];

        // 声明一个 list 记录已经查找过的节点，避免出现无限递归
        let list = [];
        while (top.hasParent()) {
            top = top.parent;
            if (list.includes(top.pid)) {
                // 出现无限递归的数据，异常退出
                throw "Circular dependencies";
            } else {
                // 继续下一次递归检索
                list.push(top.pid);
            }
        }

        return this.getNodesByPid(top.pid);
    }

    /**
     * 经过模糊搜索之后，直接按照节点 pid 进行获取
     *
     * @param pid {string|number} 值可能为空，数据库可能用 null 值表示顶级节点
     * @return [TreeNode]
     */
    getHitNodesByPid(pid) {
        let ret = [];
        const parents = this.parents;
        for (let i = 0; i < parents.length; i++) {
            let p = parents[i];
            if (p.pid === pid && p.hit) {
                ret.push(p);
            }
        }
        return ret;
    }

    /**
     * 数据检索
     *
     * @param label 模糊检索
     * @return {TreeBuilder}
     */
    search(label) {
        return this.filter(ele => ele.label.includes(label));
    }

    /**
     * 数据检索
     *
     * @param call 数据检索函数
     * @return {TreeBuilder}
     */
    filter(call) {
        // 重置状态
        this.parents.forEach(ele => ele.hit = false);
        if (this.children !== this.parents) {
            this.children.forEach(ele => ele.hit = false);
        }

        // 检测命中情况
        this.children.forEach(ele => {
            if (call(ele)) {
                ele.hit = true;

                // 如果子级命中，则父级也命中
                // 注意此处可能有无限递归的风险
                let node = ele;
                while (node.hasParent()) {
                    node = node.parent;
                    node.hit = true;
                }
            }
        });

        // 检测命中情况
        if (this.children !== this.parents) {
            this.parents.forEach(ele => {
                if (!ele.hit) {
                    ele.hit = call(ele);
                }
            });
        }

        return this;
    }
}

export default TreeBuilder;